﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

#if !RootNamespace
namespace OmniKits.Extensions
{
#endif
    public static class TextReaderEx
    {
        public static int DefaultBufferSize { get; set; } = 256;
        public const int MinimumBufferSize = 128;

        public static int EatAll(this TextReader reader, int bufferSize, bool close = false)
        {
            if (bufferSize < MinimumBufferSize)
                bufferSize = MinimumBufferSize;

            var count = 0;
            int read;
            var buffer = new char[bufferSize];
            try
            {
                while ((read = reader.Read(buffer, 0, bufferSize)) > 0)
                    count += read;
                return count;
            }
            finally
            {
                if (close) reader.Close();
            }
        }
        public static int EatAll(this TextReader reader, bool close = false)
            => reader.EatAll(DefaultBufferSize, close);

        public static int FeedCharsTo(this TextReader reader, TextWriter writer, int bufferSize, bool closeReader, bool closeWriter)
        {
            if (bufferSize < MinimumBufferSize)
                bufferSize = MinimumBufferSize;

            var count = 0;
            int read;
            var buffer = new char[bufferSize];
            try
            {
                while ((read = reader.Read(buffer, 0, bufferSize)) > 0)
                {
                    writer.Write(buffer, 0, read);
                    count += read;
                }
                return count;
            }
            finally
            {
                if (closeReader) reader.Close();
                if (closeWriter) writer.Close();
            }
        }
        public static int FeedCharsTo(this TextReader reader, TextWriter writer, int bufferSize, bool close = false)
            => reader.FeedCharsTo(writer, bufferSize, close, close);
        public static int FeedCharsTo(this TextReader reader, TextWriter writer, bool closeReader, bool closeWriter)
            => reader.FeedCharsTo(writer, DefaultBufferSize, closeReader, closeWriter);
        public static int FeedCharsTo(this TextReader reader, TextWriter writer, bool close = false)
            => reader.FeedCharsTo(writer, DefaultBufferSize, close, close);

        public static IEnumerable<int> FeedLinesTo(this TextReader reader, TextWriter writer, bool closeReader, bool closeWriter)
        {
            string line;
            try
            {
                while ((line = reader.ReadLine()) != null)
                {
                    writer.WriteLine(line);
                    yield return line.Length;
                }
            }
            finally
            {
                if (closeReader) reader.Close();
                if (closeWriter) writer.Close();
            }
        }
        public static IEnumerable<int> FeedLinesTo(this TextReader reader, TextWriter writer, bool close = false)
            => reader.FeedLinesTo(writer, close, close);

        public static int FeedAllLinesTo(this TextReader reader, TextWriter writer, bool closeReader, bool closeWriter)
            => reader.FeedLinesTo(writer, closeReader, closeWriter).Count();
        public static int FeedAllLinesTo(this TextReader reader, TextWriter writer, bool close = false)
            => reader.FeedLinesTo(writer, close, close).Count();

        public static int FeedAllTo(this TextReader reader, TextWriter writer, bool closeReader, bool closeWriter)
        {
            try
            {
                var s = reader.ReadToEnd();
                writer.Write(s);
                return s.Length;
            }
            finally
            {
                if (closeReader) reader.Close();
                if (closeWriter) writer.Close();
            }
        }
        public static int FeedAllTo(this TextReader reader, TextWriter writer, bool close = false)
            => reader.FeedAllTo(writer, close, close);

        public static string ReadLineWithEnd(this TextReader reader, bool close = false)
        {
            int read;
            char c;
            var sb = new StringBuilder();
            try
            {
                for (; ;)
                {
                    read = reader.Read();
                    if (read < 0) break;

                    c = (char)read;
                    sb.Append(c);

                    if (c == '\r')
                    {
                        read = reader.Peek();
                        if (read < 0) break;

                        c = (char)read;
                        if (c == '\n')
                        {
                            reader.Read();
                            sb.Append(c);
                        }
                        break;
                    }
                    if (c == '\n')
                        break;
                }

                if (sb.Length > 0)
                    return sb.ToString();
                return null;
            }
            finally
            {
                if (close) reader.Close();
            }
        }

        public static IEnumerable<int> FeedLinesWithEndTo(this TextReader reader, TextWriter writer, bool closeReader, bool closeWriter)
        {
            string line;
            try
            {
                while ((line = reader.ReadLineWithEnd()) != null)
                {
                    writer.Write(line);
                    yield return line.Length;
                }
            }
            finally
            {
                if (closeReader) reader.Close();
                if (closeWriter) writer.Close();
            }
        }
        public static IEnumerable<int> FeedLinesWithEndTo(this TextReader reader, TextWriter writer, bool close = false)
            => reader.FeedLinesWithEndTo(writer, close, close);

        public static int FeedAllLinesWithEndTo(this TextReader reader, TextWriter writer, bool closeReader, bool closeWriter)
            => reader.FeedLinesWithEndTo(writer, closeReader, closeWriter).Count();
        public static int FeedAllLinesWithEndTo(this TextReader reader, TextWriter writer, bool close = false)
            => reader.FeedLinesWithEndTo(writer, close, close).Count();
    }
#if !RootNamespace
}
#endif
